// 基于element-plus的视图组件
window.joEl = {};

// joEl组件
(function () {
    joEl.component = joEl.install = function (app) {
        // 注册组件
        for (var k in joEl.COMPONENT_MAP) {
            app.component(k, joEl.COMPONENT_MAP[k]);
        }
    };
    // 临时组件
    joEl.register = function (componentName, componentConfig) {
        joEl.COMPONENT_MAP[componentName] = componentConfig;
    };
    /**
     * 获取ref对象
     * @param _this vue实例,直接传this
     * @param refName ref名字
     * @returns {*|null}
     */
    joEl.getRef = function (_this, refName) {
        // 先获取子ref
        var ref = _this.$refs[refName];
        // ref无效则从根节点开始找
        if (!ref) {
            ref = joEl.findRefRecursion(_this.$root, refName);
        }
        return ref;
    };
    /**
     * 递归查找ref
     * @param refIns ref实例
     * @param refName 要查找的ref名字
     * @returns {null|*}
     */
    joEl.findRefRecursion = function (refIns, refName) {
        if (!refIns || !refIns.$refs) {
            return null;
        }
        var ref = refIns.$refs[refName];
        if (!ref) {
            for (var k in refIns.$refs) {
                ref = joEl.findRefRecursion(refIns.$refs[k], refName);
                if (ref) {
                    break;
                }
            }
        }
        return ref;
    };
    // el表单校验规则
    joEl.rules = {
        required: function (msg, trigger) {
            return {required: true, message: msg || '必填项', trigger: trigger || 'blur'};
        },
        range: function (msg, trigger, min, max) {
            return {min: min, max: max, message: msg || '超出范围', trigger: trigger || 'blur'};
        },
        length: function (msg, trigger, len) {
            return {
                validator: function (rule, value, callback) {
                    if (value && value.length > len) {
                        callback(new Error(msg || ('长度超出限制:' + len)));
                    } else {
                        callback();
                    }
                }
                , trigger: trigger || 'blur'
            };
        },
        // 是否在枚举范围内
        enums: function (msg, trigger, enumArr) {
            return {
                validator: function (rule, value, callback) {
                    var flag = false;
                    for (let i = 0; i < enumArr.length; i++) {
                        var e = enumArr[i];
                        if (e == value) {
                            flag = true;
                            break;
                        }
                    }
                    if (flag) {
                        callback();
                    } else {
                        callback(new Error(msg || '不在枚举范围内'));
                    }
                }
                , trigger: trigger || 'blur'
            };
        },
        mail: function (msg, trigger) {
            return {
                validator: function (rule, value, callback) {
                    if (value) {
                        if (!jo.Const.REG_MAIL.test(value)) {
                            callback(new Error(msg || '邮箱格式错误'));
                            return;
                        }
                    }
                    callback();
                }
                , trigger: trigger || 'blur'
            };
        },
        phone: function (msg, trigger) {
            return {
                validator: function (rule, value, callback) {
                    if (value) {
                        if (!jo.Const.REG_MOBILEPHONE.test(value)) {
                            callback(new Error(msg || '手机号格式错误'));
                            return;
                        }
                    }
                    callback();
                }
                , trigger: trigger || 'blur'
            };
        },
        reg: function (msg, trigger, reg) {
            return {
                validator: function (rule, value, callback) {
                    if (value) {
                        if (!new RegExp(reg).test(value)) {
                            callback(new Error(msg || '格式错误'));
                            return;
                        }
                    }
                    callback();
                }
                , trigger: trigger || 'blur'
            };
        },
        notNaN: function (msg, trigger) {
            return {
                validator: function (rule, value, callback) {
                    if (value) {
                        if (isNaN(value)) {
                            callback(new Error(msg || '非数错误'));
                            return;
                        }
                    }
                    callback();
                }
                , trigger: trigger || 'blur'
            };
        },


        date: function (msg, trigger) {
            return {type: 'date', message: msg || '必须是时间类型', trigger: trigger || 'blur'};
        },
        array: function (msg, trigger) {
            return {type: 'array', message: msg || '必须是数组类型', trigger: trigger || 'blur'};
        },
        object: function (msg, trigger) {
            return {type: 'object', message: msg || '必须是对象类型', trigger: trigger || 'blur'};
        },
        number: function (msg, trigger) {
            return {type: 'number', message: msg || '必须是数字类型', trigger: trigger || 'blur'};
        },
        string: function (msg, trigger) {
            return {type: 'string', message: msg || '必须是字符串类型', trigger: trigger || 'blur'};
        },
    };
    // joEl常量
    joEl.constants = {
        // 图标选项
        iconOptions: [{
            "name": "Web Application Icons",
            "icons": ["fa-address-book", "fa-address-book-o", "fa-address-card", "fa-address-card-o", "fa-adjust", "fa-american-sign-language-interpreting", "fa-anchor", "fa-archive", "fa-area-chart", "fa-arrows", "fa-arrows-h", "fa-arrows-v", "fa-asl-interpreting", "fa-assistive-listening-systems", "fa-asterisk", "fa-at", "fa-audio-description", "fa-automobile", "fa-balance-scale", "fa-ban", "fa-bank", "fa-bar-chart", "fa-bar-chart-o", "fa-barcode", "fa-bars", "fa-bath", "fa-bathtub", "fa-battery", "fa-battery-0", "fa-battery-1", "fa-battery-2", "fa-battery-3", "fa-battery-4", "fa-battery-empty", "fa-battery-full", "fa-battery-half", "fa-battery-quarter", "fa-battery-three-quarters", "fa-bed", "fa-beer", "fa-bell", "fa-bell-o", "fa-bell-slash", "fa-bell-slash-o", "fa-bicycle", "fa-binoculars", "fa-birthday-cake", "fa-blind", "fa-bluetooth", "fa-bluetooth-b", "fa-bolt", "fa-bomb", "fa-book", "fa-bookmark", "fa-bookmark-o", "fa-braille", "fa-briefcase", "fa-bug", "fa-building", "fa-building-o", "fa-bullhorn", "fa-bullseye", "fa-bus", "fa-cab", "fa-calculator", "fa-calendar", "fa-calendar-check-o", "fa-calendar-minus-o", "fa-calendar-o", "fa-calendar-plus-o", "fa-calendar-times-o", "fa-camera", "fa-camera-retro", "fa-car", "fa-caret-square-o-down", "fa-caret-square-o-left", "fa-caret-square-o-right", "fa-caret-square-o-up", "fa-cart-arrow-down", "fa-cart-plus", "fa-cc", "fa-certificate", "fa-check", "fa-check-circle", "fa-check-circle-o", "fa-check-square", "fa-check-square-o", "fa-child", "fa-circle", "fa-circle-o", "fa-circle-o-notch", "fa-circle-thin", "fa-clock-o", "fa-clone", "fa-close", "fa-cloud", "fa-cloud-download", "fa-cloud-upload", "fa-code", "fa-code-fork", "fa-coffee", "fa-cog", "fa-cogs", "fa-comment", "fa-comment-o", "fa-commenting", "fa-commenting-o", "fa-comments", "fa-comments-o", "fa-compass", "fa-copyright", "fa-creative-commons", "fa-credit-card", "fa-credit-card-alt", "fa-crop", "fa-crosshairs", "fa-cube", "fa-cubes", "fa-cutlery", "fa-dashboard", "fa-database", "fa-deaf", "fa-deafness", "fa-desktop", "fa-diamond", "fa-dot-circle-o", "fa-download", "fa-drivers-license", "fa-drivers-license-o", "fa-edit", "fa-ellipsis-h", "fa-ellipsis-v", "fa-envelope", "fa-envelope-o", "fa-envelope-open", "fa-envelope-open-o", "fa-envelope-square", "fa-eraser", "fa-exchange", "fa-exclamation", "fa-exclamation-circle", "fa-exclamation-triangle", "fa-external-link", "fa-external-link-square", "fa-eye", "fa-eye-slash", "fa-eyedropper", "fa-fax", "fa-feed", "fa-female", "fa-fighter-jet", "fa-file-archive-o", "fa-file-audio-o", "fa-file-code-o", "fa-file-excel-o", "fa-file-image-o", "fa-file-movie-o", "fa-file-pdf-o", "fa-file-photo-o", "fa-file-picture-o", "fa-file-powerpoint-o", "fa-file-sound-o", "fa-file-video-o", "fa-file-word-o", "fa-file-zip-o", "fa-film", "fa-filter", "fa-fire", "fa-fire-extinguisher", "fa-flag", "fa-flag-checkered", "fa-flag-o", "fa-flash", "fa-flask", "fa-folder", "fa-folder-o", "fa-folder-open", "fa-folder-open-o", "fa-frown-o", "fa-futbol-o", "fa-gamepad", "fa-gavel", "fa-gear", "fa-gears", "fa-gift", "fa-glass", "fa-globe", "fa-graduation-cap", "fa-group", "fa-hand-grab-o", "fa-hand-lizard-o", "fa-hand-paper-o", "fa-hand-peace-o", "fa-hand-pointer-o", "fa-hand-rock-o", "fa-hand-scissors-o", "fa-hand-spock-o", "fa-hand-stop-o", "fa-handshake-o", "fa-hard-of-hearing", "fa-hashtag", "fa-hdd-o", "fa-headphones", "fa-heart", "fa-heart-o", "fa-heartbeat", "fa-history", "fa-home", "fa-hotel", "fa-hourglass", "fa-hourglass-1", "fa-hourglass-2", "fa-hourglass-3", "fa-hourglass-end", "fa-hourglass-half", "fa-hourglass-o", "fa-hourglass-start", "fa-i-cursor", "fa-id-badge", "fa-id-card", "fa-id-card-o", "fa-image", "fa-inbox", "fa-industry", "fa-info", "fa-info-circle", "fa-institution", "fa-key", "fa-keyboard-o", "fa-language", "fa-laptop", "fa-leaf", "fa-legal", "fa-lemon-o", "fa-level-down", "fa-level-up", "fa-life-bouy", "fa-life-buoy", "fa-life-ring", "fa-life-saver", "fa-lightbulb-o", "fa-line-chart", "fa-location-arrow", "fa-lock", "fa-low-vision", "fa-magic", "fa-magnet", "fa-mail-forward", "fa-mail-reply", "fa-mail-reply-all", "fa-male", "fa-map", "fa-map-marker", "fa-map-o", "fa-map-pin", "fa-map-signs", "fa-meh-o", "fa-microchip", "fa-microphone", "fa-microphone-slash", "fa-minus", "fa-minus-circle", "fa-minus-square", "fa-minus-square-o", "fa-mobile", "fa-mobile-phone", "fa-money", "fa-moon-o", "fa-mortar-board", "fa-motorcycle", "fa-mouse-pointer", "fa-music", "fa-navicon", "fa-newspaper-o", "fa-object-group", "fa-object-ungroup", "fa-paint-brush", "fa-paper-plane", "fa-paper-plane-o", "fa-paw", "fa-pencil", "fa-pencil-square", "fa-pencil-square-o", "fa-percent", "fa-phone", "fa-phone-square", "fa-photo", "fa-picture-o", "fa-pie-chart", "fa-plane", "fa-plug", "fa-plus", "fa-plus-circle", "fa-plus-square", "fa-plus-square-o", "fa-podcast", "fa-power-off", "fa-print", "fa-puzzle-piece", "fa-qrcode", "fa-question", "fa-question-circle", "fa-question-circle-o", "fa-quote-left", "fa-quote-right", "fa-random", "fa-recycle", "fa-refresh", "fa-registered", "fa-remove", "fa-reorder", "fa-reply", "fa-reply-all", "fa-retweet", "fa-road", "fa-rocket", "fa-rss", "fa-rss-square", "fa-s15", "fa-search", "fa-search-minus", "fa-search-plus", "fa-send", "fa-send-o", "fa-server", "fa-share", "fa-share-alt", "fa-share-alt-square", "fa-share-square", "fa-share-square-o", "fa-shield", "fa-ship", "fa-shopping-bag", "fa-shopping-basket", "fa-shopping-cart", "fa-shower", "fa-sign-in", "fa-sign-language", "fa-sign-out", "fa-signal", "fa-signing", "fa-sitemap", "fa-sliders", "fa-smile-o", "fa-snowflake-o", "fa-soccer-ball-o", "fa-sort", "fa-sort-alpha-asc", "fa-sort-alpha-desc", "fa-sort-amount-asc", "fa-sort-amount-desc", "fa-sort-asc", "fa-sort-desc", "fa-sort-down", "fa-sort-numeric-asc", "fa-sort-numeric-desc", "fa-sort-up", "fa-space-shuttle", "fa-spinner", "fa-spoon", "fa-square", "fa-square-o", "fa-star", "fa-star-half", "fa-star-half-empty", "fa-star-half-full", "fa-star-half-o", "fa-star-o", "fa-sticky-note", "fa-sticky-note-o", "fa-street-view", "fa-suitcase", "fa-sun-o", "fa-support", "fa-tablet", "fa-tachometer", "fa-tag", "fa-tags", "fa-tasks", "fa-taxi", "fa-television", "fa-terminal", "fa-thermometer", "fa-thermometer-0", "fa-thermometer-1", "fa-thermometer-2", "fa-thermometer-3", "fa-thermometer-4", "fa-thermometer-empty", "fa-thermometer-full", "fa-thermometer-half", "fa-thermometer-quarter", "fa-thermometer-three-quarters", "fa-thumb-tack", "fa-thumbs-down", "fa-thumbs-o-down", "fa-thumbs-o-up", "fa-thumbs-up", "fa-ticket", "fa-times", "fa-times-circle", "fa-times-circle-o", "fa-times-rectangle", "fa-times-rectangle-o", "fa-tint", "fa-toggle-down", "fa-toggle-left", "fa-toggle-off", "fa-toggle-on", "fa-toggle-right", "fa-toggle-up", "fa-trademark", "fa-trash", "fa-trash-o", "fa-tree", "fa-trophy", "fa-truck", "fa-tty", "fa-tv", "fa-umbrella", "fa-universal-access", "fa-university", "fa-unlock", "fa-unlock-alt", "fa-unsorted", "fa-upload", "fa-user", "fa-user-circle", "fa-user-circle-o", "fa-user-o", "fa-user-plus", "fa-user-secret", "fa-user-times", "fa-users", "fa-vcard", "fa-vcard-o", "fa-video-camera", "fa-volume-control-phone", "fa-volume-down", "fa-volume-off", "fa-volume-up", "fa-warning", "fa-wheelchair", "fa-wheelchair-alt", "fa-wifi", "fa-window-close", "fa-window-close-o", "fa-window-maximize", "fa-window-minimize", "fa-window-restore", "fa-wrench"]
        }, {
            "name": "Accessibility Icons",
            "icons": ["fa-american-sign-language-interpreting", "fa-asl-interpreting", "fa-assistive-listening-systems", "fa-audio-description", "fa-blind", "fa-braille", "fa-cc", "fa-deaf", "fa-deafness", "fa-hard-of-hearing", "fa-low-vision", "fa-question-circle-o", "fa-sign-language", "fa-signing", "fa-tty", "fa-universal-access", "fa-volume-control-phone", "fa-wheelchair", "fa-wheelchair-alt"]
        }, {
            "name": "Hand Icons",
            "icons": ["fa-hand-grab-o", "fa-hand-lizard-o", "fa-hand-o-down", "fa-hand-o-left", "fa-hand-o-right", "fa-hand-o-up", "fa-hand-paper-o", "fa-hand-peace-o", "fa-hand-pointer-o", "fa-hand-rock-o", "fa-hand-scissors-o", "fa-hand-spock-o", "fa-hand-stop-o", "fa-thumbs-down", "fa-thumbs-o-down", "fa-thumbs-o-up", "fa-thumbs-up"]
        }, {
            "name": "Transportation Icons",
            "icons": ["fa-ambulance", "fa-automobile", "fa-bicycle", "fa-bus", "fa-cab", "fa-car", "fa-fighter-jet", "fa-motorcycle", "fa-plane", "fa-rocket", "fa-ship", "fa-space-shuttle", "fa-subway", "fa-taxi", "fa-train", "fa-truck", "fa-wheelchair", "fa-wheelchair-alt"]
        }, {
            "name": "Gender Icons",
            "icons": ["fa-genderless", "fa-intersex", "fa-mars", "fa-mars-double", "fa-mars-stroke", "fa-mars-stroke-h", "fa-mars-stroke-v", "fa-mercury", "fa-neuter", "fa-transgender", "fa-transgender-alt", "fa-venus", "fa-venus-double", "fa-venus-mars"]
        }, {
            "name": "File Type Icons",
            "icons": ["fa-file", "fa-file-archive-o", "fa-file-audio-o", "fa-file-code-o", "fa-file-excel-o", "fa-file-image-o", "fa-file-movie-o", "fa-file-o", "fa-file-pdf-o", "fa-file-photo-o", "fa-file-picture-o", "fa-file-powerpoint-o", "fa-file-sound-o", "fa-file-text", "fa-file-text-o", "fa-file-video-o", "fa-file-word-o", "fa-file-zip-o"]
        }, {
            "name": "Spinner Icons",
            "icons": ["fa-circle-o-notch", "fa-cog", "fa-gear", "fa-refresh", "fa-spinner"]
        }, {
            "name": "Form Control Icons",
            "icons": ["fa-check-square", "fa-check-square-o", "fa-circle", "fa-circle-o", "fa-dot-circle-o", "fa-minus-square", "fa-minus-square-o", "fa-plus-square", "fa-plus-square-o", "fa-square", "fa-square-o"]
        }, {
            "name": "Payment Icons",
            "icons": ["fa-cc-amex", "fa-cc-diners-club", "fa-cc-discover", "fa-cc-jcb", "fa-cc-mastercard", "fa-cc-paypal", "fa-cc-stripe", "fa-cc-visa", "fa-credit-card", "fa-credit-card-alt", "fa-google-wallet", "fa-paypal"]
        }, {
            "name": "Chart Icons",
            "icons": ["fa-area-chart", "fa-bar-chart", "fa-bar-chart-o", "fa-line-chart", "fa-pie-chart"]
        }, {
            "name": "Currency Icons",
            "icons": ["fa-bitcoin", "fa-btc", "fa-cny", "fa-dollar", "fa-eur", "fa-euro", "fa-gbp", "fa-gg", "fa-gg-circle", "fa-ils", "fa-inr", "fa-jpy", "fa-krw", "fa-money", "fa-rmb", "fa-rouble", "fa-rub", "fa-ruble", "fa-rupee", "fa-shekel", "fa-sheqel", "fa-try", "fa-turkish-lira", "fa-usd", "fa-won", "fa-yen"]
        }, {
            "name": "Text Editor Icons",
            "icons": ["fa-align-center", "fa-align-justify", "fa-align-left", "fa-align-right", "fa-bold", "fa-chain", "fa-chain-broken", "fa-clipboard", "fa-columns", "fa-copy", "fa-cut", "fa-dedent", "fa-eraser", "fa-file", "fa-file-o", "fa-file-text", "fa-file-text-o", "fa-files-o", "fa-floppy-o", "fa-font", "fa-header", "fa-indent", "fa-italic", "fa-link", "fa-list", "fa-list-alt", "fa-list-ol", "fa-list-ul", "fa-outdent", "fa-paperclip", "fa-paragraph", "fa-paste", "fa-repeat", "fa-rotate-left", "fa-rotate-right", "fa-save", "fa-scissors", "fa-strikethrough", "fa-subscript", "fa-superscript", "fa-table", "fa-text-height", "fa-text-width", "fa-th", "fa-th-large", "fa-th-list", "fa-underline", "fa-undo", "fa-unlink"]
        }, {
            "name": "Directional Icons",
            "icons": ["fa-angle-double-down", "fa-angle-double-left", "fa-angle-double-right", "fa-angle-double-up", "fa-angle-down", "fa-angle-left", "fa-angle-right", "fa-angle-up", "fa-arrow-circle-down", "fa-arrow-circle-left", "fa-arrow-circle-o-down", "fa-arrow-circle-o-left", "fa-arrow-circle-o-right", "fa-arrow-circle-o-up", "fa-arrow-circle-right", "fa-arrow-circle-up", "fa-arrow-down", "fa-arrow-left", "fa-arrow-right", "fa-arrow-up", "fa-arrows", "fa-arrows-alt", "fa-arrows-h", "fa-arrows-v", "fa-caret-down", "fa-caret-left", "fa-caret-right", "fa-caret-square-o-down", "fa-caret-square-o-left", "fa-caret-square-o-right", "fa-caret-square-o-up", "fa-caret-up", "fa-chevron-circle-down", "fa-chevron-circle-left", "fa-chevron-circle-right", "fa-chevron-circle-up", "fa-chevron-down", "fa-chevron-left", "fa-chevron-right", "fa-chevron-up", "fa-exchange", "fa-hand-o-down", "fa-hand-o-left", "fa-hand-o-right", "fa-hand-o-up", "fa-long-arrow-down", "fa-long-arrow-left", "fa-long-arrow-right", "fa-long-arrow-up", "fa-toggle-down", "fa-toggle-left", "fa-toggle-right", "fa-toggle-up"]
        }, {
            "name": "Video Player Icons",
            "icons": ["fa-arrows-alt", "fa-backward", "fa-compress", "fa-eject", "fa-expand", "fa-fast-backward", "fa-fast-forward", "fa-forward", "fa-pause", "fa-pause-circle", "fa-pause-circle-o", "fa-play", "fa-play-circle", "fa-play-circle-o", "fa-random", "fa-step-backward", "fa-step-forward", "fa-stop", "fa-stop-circle", "fa-stop-circle-o", "fa-youtube-play"]
        }, {
            "name": "Brand Icons",
            "icons": ["fa-500px", "fa-adn", "fa-amazon", "fa-android", "fa-angellist", "fa-apple", "fa-bandcamp", "fa-behance", "fa-behance-square", "fa-bitbucket", "fa-bitbucket-square", "fa-bitcoin", "fa-black-tie", "fa-bluetooth", "fa-bluetooth-b", "fa-btc", "fa-buysellads", "fa-cc-amex", "fa-cc-diners-club", "fa-cc-discover", "fa-cc-jcb", "fa-cc-mastercard", "fa-cc-paypal", "fa-cc-stripe", "fa-cc-visa", "fa-chrome", "fa-codepen", "fa-codiepie", "fa-connectdevelop", "fa-contao", "fa-css3", "fa-dashcube", "fa-delicious", "fa-deviantart", "fa-digg", "fa-dribbble", "fa-dropbox", "fa-drupal", "fa-edge", "fa-eercast", "fa-empire", "fa-envira", "fa-etsy", "fa-expeditedssl", "fa-fa", "fa-facebook", "fa-facebook-f", "fa-facebook-official", "fa-facebook-square", "fa-firefox", "fa-first-order", "fa-flickr", "fa-font-awesome", "fa-fonticons", "fa-fort-awesome", "fa-forumbee", "fa-foursquare", "fa-free-code-camp", "fa-ge", "fa-get-pocket", "fa-gg", "fa-gg-circle", "fa-git", "fa-git-square", "fa-github", "fa-github-alt", "fa-github-square", "fa-gitlab", "fa-gittip", "fa-glide", "fa-glide-g", "fa-google", "fa-google-plus", "fa-google-plus-circle", "fa-google-plus-official", "fa-google-plus-square", "fa-google-wallet", "fa-gratipay", "fa-grav", "fa-hacker-news", "fa-houzz", "fa-html5", "fa-imdb", "fa-instagram", "fa-internet-explorer", "fa-ioxhost", "fa-joomla", "fa-jsfiddle", "fa-lastfm", "fa-lastfm-square", "fa-leanpub", "fa-linkedin", "fa-linkedin-square", "fa-linode", "fa-linux", "fa-maxcdn", "fa-meanpath", "fa-medium", "fa-meetup", "fa-mixcloud", "fa-modx", "fa-odnoklassniki", "fa-odnoklassniki-square", "fa-opencart", "fa-openid", "fa-opera", "fa-optin-monster", "fa-pagelines", "fa-paypal", "fa-pied-piper", "fa-pied-piper-alt", "fa-pied-piper-pp", "fa-pinterest", "fa-pinterest-p", "fa-pinterest-square", "fa-product-hunt", "fa-qq", "fa-quora", "fa-ra", "fa-ravelry", "fa-rebel", "fa-reddit", "fa-reddit-alien", "fa-reddit-square", "fa-renren", "fa-resistance", "fa-safari", "fa-scribd", "fa-sellsy", "fa-share-alt", "fa-share-alt-square", "fa-shirtsinbulk", "fa-simplybuilt", "fa-skyatlas", "fa-skype", "fa-slack", "fa-slideshare", "fa-snapchat", "fa-snapchat-ghost", "fa-snapchat-square", "fa-soundcloud", "fa-spotify", "fa-stack-exchange", "fa-stack-overflow", "fa-steam", "fa-steam-square", "fa-stumbleupon", "fa-stumbleupon-circle", "fa-superpowers", "fa-telegram", "fa-tencent-weibo", "fa-themeisle", "fa-trello", "fa-tripadvisor", "fa-tumblr", "fa-tumblr-square", "fa-twitch", "fa-twitter", "fa-twitter-square", "fa-usb", "fa-viacoin", "fa-viadeo", "fa-viadeo-square", "fa-vimeo", "fa-vimeo-square", "fa-vine", "fa-vk", "fa-wechat", "fa-weibo", "fa-weixin", "fa-whatsapp", "fa-wikipedia-w", "fa-windows", "fa-wordpress", "fa-wpbeginner", "fa-wpexplorer", "fa-wpforms", "fa-xing", "fa-xing-square", "fa-y-combinator", "fa-y-combinator-square", "fa-yahoo", "fa-yc", "fa-yc-square", "fa-yelp", "fa-yoast", "fa-youtube", "fa-youtube-play", "fa-youtube-square"]
        }, {
            "name": "Medical Icons",
            "icons": ["fa-ambulance", "fa-h-square", "fa-heart", "fa-heart-o", "fa-heartbeat", "fa-hospital-o", "fa-medkit", "fa-plus-square", "fa-stethoscope", "fa-user-md", "fa-wheelchair", "fa-wheelchair-alt"]
        }],
    };
})();


// joEl组件
(function () {
    joEl.COMPONENT_MAP = {
        'jo-el-tag': {
            props: {
                // tag值
                value: {
                    type: [String, Number],
                    default: ''
                },
                // 枚举选项
                options: {
                    type: [Array, Object],
                    default: function () {
                        return {};
                    }
                },
                // 枚举选项中的code字段名
                codeField: {
                    type: String,
                    default: 'code'
                },
                // 枚举选项中的展示字段名
                textField: {
                    type: String,
                    default: 'text'
                },
                // 枚举选项中的样式字段
                typeField: {
                    type: String,
                    default: 'styleType'
                },
                // el-tag样式effect
                effect: {
                    type: String,
                    default: 'plain'
                },
                // el-tag皮肤样式type, '',success,info,warning,danger
                type: {
                    type: String,
                    default: ''
                },
            },
            data: function () {
                return {};
            },
            computed: {
                // 是否显示
                showFlag() {
                    return jo.isValid(this.contentValue);
                },
                // tag展示内容
                contentValue() {
                    if (this.options) {
                        if (Array.isArray(this.options) && this.options.length > 0) {
                            for (let i = 0; i < this.options.length; i++) {
                                var item = this.options[i];
                                if (item[this.codeField] == this.value) {
                                    return item[this.textField];
                                }
                            }
                        } else if (typeof this.options == 'object') {
                            if (this.value in this.options) {
                                return this.options[this.value];
                            }
                        }
                    }
                    return this.value;
                },
                // tag的type值, 从枚举选项中拿
                typeValue() {
                    var type = this.type;
                    if (!this.type) {
                        // 没有指定样式, 则到枚举里面找
                        if (Array.isArray(this.options) && this.options.length > 0) {
                            for (let i = 0; i < this.options.length; i++) {
                                var item = this.options[i];
                                if (item[this.codeField] == this.value) {
                                    type = jo.getDefVal(item[this.typeField], this.type);
                                    break;
                                }
                            }
                        }
                    }
                    // 在tag组件里面, primary要设置空
                    return type === 'primary' ? '' : type;
                }
            },
            template: `
                <el-tag v-if="showFlag" :type="typeValue" :effect="effect">{{ contentValue }}</el-tag>
            `,
            methods: {},
            mounted() {
                console.info('[jo-el-tag] mounted');
            }
        },
        'jo-el-table': {
            // 可以接受的属性
            props: {
                tableRef: {
                    type: String,
                    default: 'tableRef'
                },
                // 数据url
                url: {
                    type: String
                },
                // 列配置
                cols: {
                    type: Array
                },
                // 是否开启分页
                pageEnable: {
                    type: [String, Number, Boolean],
                    default: true
                },
                // 边框
                border: {
                    type: Boolean,
                    default: false
                },
                // 查询参数对象
                searchParam: {
                    type: Object,
                    default: function () {
                        return {};
                    }
                },
                // 渲染表格数据前通过该方法处理数据项
                handleItem: {
                    type: Function,
                },
                // 查询参数处理, 查询前对参数的校准
                handleParam: {
                    type: Function,
                },
                pageSize: {
                    type: Number,
                    default: 10
                },
                pageSizes: {
                    type: Array,
                    default: [10, 20, 50, 100]
                },
                // 分页布局
                pageLayout: {
                    type: String,
                    default: 'total, sizes, prev, pager, next'
                },
                // 首列类型, radio,checkbox,index,none
                firstColumn: {
                    type: String,
                    default: 'none'
                },
                // 是否开启加载提示
                enableLoading: {
                    type: Boolean,
                    default: false
                },
                // radio首列初始化值
                radioInitValue: {
                    type: [String, Number],
                    default: null
                },
                // 数据加载后回调
                dataLoadAfter: {
                    type: Function,
                    default: null
                },
            },
            data: function () {
                return {
                    tableData: [],
                    page: {
                        pageNumber: 1,
                        pageSize: this.pageSize,
                        pageSizes: this.pageSizes,
                        total: 0,
                        layout: this.pageLayout,   //, jumper
                        hideOnSinglePage: false
                    },
                    // 排序字段
                    sortBy: '',
                    // 排序类型, asc,desc
                    sortType: '',

                    // radio单选框值对象引用
                    radioValue: {
                        value: this.radioInitValue
                    },

                    // 数据加载中状态
                    dataLoading: false,
                };
            },
            computed: {
                // 首列是单选框
                isRadio() {
                    return this.firstColumn === 'radio';
                },
                // 是否分页展示
                showPageBar() {
                    if (this.pageEnable === false
                        || this.pageEnable === 'false'
                        || this.pageEnable === 0) {
                        return false;
                    }
                    return true;
                },
                // loading状态
                realLoadingStatus() {
                    // 开启loading后生效
                    return this.enableLoading && this.dataLoading;
                }
            },
            template: `
            <div class="jo-el-table-bar" v-loading="realLoadingStatus">
                <div class="jo-el-table-data-bar">
                    <slot :cols="cols" :data="tableData" :radio="radioValue" :ref="tableRef">
                        <el-table :ref="tableRef" :data="tableData" :border="border" header-cell-class-name="jo-el-table-header" 
                            @sort-change="tableSortChange">
                            <slot name="columns" :cols="cols">
                                   <el-table-column v-for="col in cols" :type="col.type" :prop="col.field" :label="col.label"
                                    :width="col.width" :header-align="col.headerAlign" :align="col.align" :formatter="col.formatter">
                                    </el-table-column>
                                    <slot name="oper"></slot>
                            </slot>
                        </el-table>
                    </slot>
                </div>
                <div class="jo-el-table-page-bar" v-if="showPageBar">
                    <slot name="page" :page="page">
                          <el-pagination
                            :currentPage="page.pageNumber"
                            :page-sizes="page.pageSizes"
                            :page-size="page.pageSize"
                            :total="page.total"
                            :layout="page.layout"
                            :background="true"
                            :hide-on-single-page="page.hideOnSinglePage"
                            @size-change="handleSizeChange"
                            @current-change="goPage"
                            class=""
                            small
                            >
                          </el-pagination>
                    </slot>
                </div>
            </div>
            `,
            methods: {
                // 排序{ column, prop, order }
                tableSortChange(sort) {
                    if (sort) {
                        this.sortBy = jo.getDefVal(sort.prop, '');
                        if (sort.order === 'ascending') {
                            // 升序
                            this.sortType = 'asc';
                        } else if (sort.order === 'descending') {
                            // 降序
                            this.sortType = 'desc';
                        } else {
                            this.sortType = '';
                        }
                        // 刷新表格数据
                        this.refresh();
                    }
                },
                // 获取表格ref
                getTableRef() {
                    return joEl.getRef(this, this.tableRef);
                },
                // 获取radio选中项
                getCheckedRadioItem() {
                    if (jo.isValid(this.radioValue.value)) {
                        return this.tableData[this.radioValue.value];
                    }
                    return null;
                },
                // 获取选中项
                getCheckedItemArr() {
                    if (this.isRadio) {
                        var item = this.getCheckedRadioItem();
                        return item ? [item] : [];
                    } else {
                        var table = this.getTableRef();
                        if (!table) {
                            console.error('[jo-el-table] 获取el表格ref为空,tableRef参数=' + this.tableRef);
                            return [];
                        }
                        var arr = table.getSelectionRows();
                        return arr ? arr : [];
                    }
                },
                // 获取选中项某字段集合
                getCheckedItemFieldArr(field) {
                    var arr = [];
                    field = jo.getDefVal(field, 'id');
                    jo.forEach(this.getCheckedItemArr(), function (item) {
                        arr.push(item[field]);
                    });
                    return arr;
                },
                // 页大小变更
                handleSizeChange(pageSize) {
                    this.searchInvoke({pageNumber: 1, pageSize: pageSize});
                },
                // 去第n页
                goPage(pageNumber) {
                    if (!pageNumber) {
                        pageNumber = 1;
                    }
                    this.searchInvoke({pageNumber: pageNumber});
                },
                // 刷新数据, 也就是重新加载当前页
                refresh() {
                    this.goPage(this.page.pageNumber);
                },
                // 数据查询执行核心方法
                searchInvoke(extParam) {
                    var _this = this;
                    // 设置加载中状态
                    this.dataLoading = true;
                    try {
                        // 构造查询参数, 包括表单参数和分页参数等
                        var param = _this.buildSearchParam(extParam);
                        var url = _this.url;// + jo.getLinkSign(_this.url) + 'pageNumber=' + jo.getDefVal(param.pageNumber, '') + '&pageSize=' + jo.getDefVal(param.pageSize, '')
                        jo.postJsonAjax(url, param).success(function (json) {
                            var list = json.data;
                            jo.forEach(list, function (item, i) {
                                if (typeof _this.handleItem == 'function') {
                                    _this.handleItem(item, i);
                                }
                            });
                            _this.tableData = json.data || [];
                            _this.page.pageNumber = json.pageNumber;
                            _this.page.total = json.total;
                            // 清空loading状态
                            _this.dataLoading = false;
                            // 数据加载后回调
                            if (typeof _this.dataLoadAfter == 'function') {
                                _this.dataLoadAfter(json, _this);
                            }
                        }).error(function (json) {
                            // 清空loading状态
                            _this.dataLoading = false;
                            console.warn('[jo-el-table] query error', json);
                        });
                    } catch (e) {
                        // 清空loading状态
                        _this.dataLoading = false;
                        console.warn('[jo-el-table] 数据查询searchInvoke执行异常:', e);
                    }
                },
                // 构造查询参数
                buildSearchParam(extParam) {
                    // 输入的查询条件
                    var param = {
                        body: this.searchParam ? JSON.parse(JSON.stringify(this.searchParam)) : {}
                    };
                    // 分页参数
                    param.pageNumber = this.page.pageNumber;
                    param.pageSize = this.page.pageSize;
                    // 排序参数
                    if (this.sortBy) {
                        param.body.dbSortBy = this.sortBy;
                    }
                    if (this.sortType) {
                        param.body.dbSortType = this.sortType;
                    }
                    // 指定的扩展参数, 和上面参数重复的, 以扩展参数为准, 可以用来参数校准
                    if (extParam) {
                        param = jo.mergeObject(param, extParam);
                    }
                    // 参数处理钩子
                    if (typeof this.handleParam == 'function') {
                        this.handleParam(param);
                    }
                    return param;
                }
            },
            mounted() {
                this.goPage(1);
                console.info('[jo-el-table] mounted');
            }
        },
        'jo-el-data': {
            props: {
                url: {
                    type: String,
                    default: ''
                },
                // 绑定根属性名
                bindRootName: {
                    type: [String],
                    default: ''
                },
                // 数据查询参数
                param: {
                    type: [String, Number, Object, Array],
                    default: {}
                }
            },
            data: function () {
                return {
                    data: []
                };
            },
            template: `<div class="jo-el-data-bar"><slot :data="data" :url="url"></slot></div>`,
            mounted() {
                var _this = this;
                if (this.url) {
                    jo.postJsonAjax(this.url, {body: this.param || {}}).success(function (json) {
                        _this.data = json.data;
                        if (_this.bindRootName) {
                            _this.$root[_this.bindRootName] = json.data;
                        }
                    }).error(function (json) {
                        console.warn('[jo-el-data] error, url: ' + _this.url, json);
                    });
                }
                console.info('[jo-el-data] mounted, url: ' + this.url);
            }
        },
        'jo-el-dialog-form': {
            emits: ['ok'],
            props: {
                // 表单ref
                formRef: {
                    type: String,
                    default: 'joElDialogFormRef'
                },
                // // 表单数据
                // data: {
                //     type: Object,
                //     default: function () {
                //         return {};
                //     }
                // },
                // 组件实例操作指令对象, 方便调用组件实例提供的方法
                // command: {
                //     type: Object,
                //     default: function () {
                //         return {};
                //     }
                // },
                instance: {
                    type: Object,
                    default: function () {
                        return {
                            command: {},
                            call: {},
                        };
                    }
                },
                // 弹层标题
                title: {
                    type: String,
                    default: ''
                },
                // 弹层宽度
                dialogWidth: {
                    type: [Number, String],
                    default: '60%'
                },
                // 表单项标题位置
                labelPosition: {
                    type: String,
                    default: 'right'
                },
                // 表单项标题宽度
                labelWidth: {
                    type: [Number, String],
                    default: '100px'
                },
                // 表单校验规则
                rules: {
                    type: Object,
                    default: function () {
                        return {};
                    }
                },
            },
            data: function () {
                return {
                    // 弹层展示标识
                    showDialog: false,
                    // 表单数据
                    data: {},
                    // // 实例内部对象
                    // instanceInner: {
                    //     command: this.instance.command || {},
                    //     call: this.instance.call || {},
                    // }
                };
            },
            computed: {
                // 组件实例操作指令对象, 方便调用组件实例提供的方法
                command() {
                    return this.instance.command;
                },
                // 钩子函数
                call() {
                    return this.instance.call;
                },
            },
            template: `
            <el-dialog v-model="showDialog" :title="title" :width="dialogWidth">
                <el-form :ref="formRef" :model="data" :label-position="labelPosition" :rules="rules" :label-width="labelWidth">
                    <slot :data="data"></slot>
                </el-form>
                <template #footer>
                    <div>
                        <slot name="footer">
                            <el-button @click="close">取消</el-button>
                            <el-button type="primary" @click="ok">确认</el-button>
                        </slot>
                    </div>
                </template>
            </el-dialog>
            `,
            methods: {
                // 获取实例对象
                getRef() {
                    return this;
                },
                // 获取表单ref对象
                getFormRef() {
                    return this.$refs[this.formRef];
                },
                // 获取表单数据
                getData() {
                    return this.data;
                },
                // 打开弹层
                open(data) {
                    // 弹层打开前构造表单数据
                    if (typeof this.call.buildData == 'function') {
                        data = this.call.buildData(data || {});
                    }
                    // 设置表单数据
                    this.data = data || {};
                    // 展示
                    this.showDialog = true;
                },
                // 关闭弹层
                close() {
                    // 关闭弹层前清理数据
                    if (typeof this.call.cleanData == 'function') {
                        this.call.cleanData(this.data);
                    }
                    // 关闭
                    this.showDialog = false;
                },
                // 确定按钮
                ok() {
                    // 触发ok事件
                    this.$emit('ok', this.data, this.command);
                },
                // 初始化instance属性
                initInstance() {
                    if (!this.instance.command) {
                        this.instance.command = {};
                    }
                    if (!this.instance.call) {
                        this.instance.call = {};
                    }

                    // 绑定命令对象
                    for (let k in this) {
                        if (typeof this[k] == "function") {
                            this.command[k] = this[k];
                            console.info('[jo-el-dialog-form] 绑定命令:', k);
                        }
                    }
                },
            },
            mounted() {
                // 初始化实例对象
                this.initInstance();
                console.info('[jo-el-dialog-form] mounted', this.command);
            }
        },
        // 文件上传按钮
        'jo-el-upload-btn': {
            emits: ['fileChange', 'submitUpload'],
            props: {
                // 按钮plain样式
                plain: {
                    type: Boolean,
                    default: true
                },
                // title
                title: {
                    type: String,
                    default: '上传'
                },
                // 宽度
                width: {
                    type: String,
                    default: '500px'
                },
                // 是否可以多选文件
                multiple: {
                    type: Boolean,
                    default: true
                },
                // 模板下载url
                templateUrl: {
                    type: String,
                    default: ''
                }
            },
            data: function () {
                return {
                    // 选中的文件
                    files: [],
                    // 上传弹层
                    layerShow: false,
                    // id
                    fileInputId: 'file_upload_' + jo.getUUID(16)
                };
            },
            computed: {

            },
            template: `
            <el-button type="success" @click="uploadClick" :plain="plain">
                <slot>上传</slot>
                <el-dialog v-model="layerShow" :width="width">
                    <template #header>
                        <div style="text-align: left;color: #333333;font-size: 16px;">{{title}}</div>
                    </template>
                    <div style="text-align: left;">
                        <div v-for="(item, idx) in files" style="padding: 5px;display: flex;">
                            <div style="flex: 1;"><i class="fa fa-file-text-o"></i> {{item.name}}</div>
                            <div><i class="fa fa-close" style="color: #f56c6c;" @click="removeInner(idx)"></i></div>
                        </div>
                    </div>
                    <template #footer>
                        <span class="dialog-footer">
<!--                            <el-button @click="closeLayer">取消</el-button>-->
                            <el-button v-if="templateUrl" type="info" @click="templateDownloadInner">模板下载</el-button>
                            <el-button type="success" @click="selectFileClick">选择文件</el-button>
                            <el-button type="primary" @click="submitUploadInner">确认</el-button>
                        </span>
                        <input :id="fileInputId" type="file" @change="fileChangeInner" :multiple="multiple" style="width: 0px;height: 0px;">
                    </template>
                </el-dialog>
            </el-button>
            
            `,
            methods: {
                // 上传点击
                uploadClick() {
                    this.files = [];//清空上一次选项
                    this.layerShow = true;
                },
                // 关闭
                closeLayer() {
                    this.layerShow = false;
                },
                // 确认
                submitUploadInner() {
                    if (!this.files || this.files.length === 0) {
                        jo.showTipsMsg('请选择待处理文件~');
                        return;
                    }
                    this.$emit('submitUpload', this.files);
                    this.closeLayer();
                },
                // 选择文件
                selectFileClick() {
                    document.getElementById(this.fileInputId).click();
                },
                // 文件发生变化, 说明用户重新选了文件
                fileChangeInner(event) {
                    if (event && event.target && event.target.files && event.target.files.length > 0) {
                        var map = jo.array2Object(this.files, function (item) {
                            return item.name + '_' + item.size + '_' + item.lastModified;
                        }, function (item) {
                            return item;
                        });
                        var changeFlag = false;
                        var changeFiles = [];
                        jo.forEach(event.target.files, (item, idx) => {
                            if (!map[item.name + '_' + item.size + '_' + item.lastModified]) {
                                this.files.push(item);
                                changeFlag = true;
                                changeFiles.push(item);
                            } else {
                                console.warn('[jo-el-upload-btn] 当前文件已被选择', item);
                            }
                        });
                        // this.files = event.target.files;
                        console.info('[jo-el-upload-btn] 当前所有文件', this.files);
                        if (changeFlag) {
                            this.$emit('fileChange', this.files, changeFiles);
                        }
                    } else {
                        console.warn('[jo-el-upload-btn] 文件变更获取文件失败', event);
                    }
                },
                // 移除文件
                removeInner(idx) {
                    this.files.splice(idx, 1);
                    var file = this.files[idx];
                    this.$emit('fileChange', this.files, [file]);
                },
                // 模板下载
                templateDownloadInner() {
                    if (this.templateUrl) {
                        jo.newWindow(this.templateUrl);
                    }
                }
            },
            mounted() {
                console.info('[jo-el-upload-btn] mounted');
            }
        },
    };
})();

// joEl方法库
(function () {
    // 当key不存在时赋值
    joEl.putIfNoValue = function (obj, key, value) {
        if (!jo.isValid(key)) {
            console.warn('当前key无效:' + key);
        } else if (jo.isValid(obj[key])) {
            console.info('当前key已存在:' + key + ', 不替换原属性');
        } else {
            obj[key] = value;
        }
    };

    /**
     * 构造vue参数, 多个参数合并为一个参数, 优先级为入参顺序
     * @param param 定制参数
     * @param commonParam 合并参数
     * @returns {*}
     */
    joEl.buildVueAppParam = function (param, commonParam) {
        if (arguments.length > 2) {
            for (var i = 1; i < arguments.length; i++) {
                var p = arguments[i];
                param = joEl.buildVueAppParamInvoke(param, p, i);
            }
            return param;
        } else if (commonParam) {
            return joEl.buildVueAppParamInvoke(param, commonParam);
        } else {
            return param;
        }
    };

    // 实际执行vue参数合并函数, idx表示序号参数合并序号, 从1开始
    joEl.buildVueAppParamInvoke = function (param, commonParam, idx) {
        if (!commonParam) {
            return param;
        }
        // 初始化方法
        if (!param.methods) {
            param.methods = {};
        }
        idx = jo.getDefVal(idx, 1);
        var preData = 'data__backup';
        var preCommonData = 'common__data__backup';
        // 暂存定制data方法
        param.methods[preData + idx] = param.data || function () {
            console.info('默认空data:' + preData + idx);
            return {};
        };
        param.methods[preCommonData + idx] = commonParam.data || function () {
            console.info('默认空data:' + preCommonData + idx);
            return {};
        };
        // 重写data
        param.data = function () {
            var common = typeof this[preCommonData + idx] == 'function' ? this[preCommonData + idx]() : {};
            var data = typeof this[preData + idx] == 'function' ? this[preData + idx]() : {};
            var result = jo.mergeObject(common, data);
            console.debug('当前data的序号为=%s,公共data=%o,基础data=%o,合并后data=%o', idx, common, data, result);
            return result;
        };

        // 初始化计算变量
        if (!param.computed) {
            param.computed = {};
        }
        if (commonParam.computed) {
            for (var k in commonParam.computed) {
                // 不存在则填充计算变量
                joEl.putIfNoValue(param.computed, k, commonParam.computed[k]);
            }
        }

        // 填充方法
        if (commonParam.methods) {
            for (var k in commonParam.methods) {
                // 不存在则填充方法
                joEl.putIfNoValue(param.methods, k, commonParam.methods[k]);
            }
        }

        // mounted
        if (!param.mounted) {
            param.mounted = commonParam.mounted;
        }

        // watch
        if (!param.watch) {
            param.watch = commonParam.watch;
        }

        return param;
    };
})();

// joEl基础app参数
(function () {
    // 公共参数
    joEl.VUE_COMMON = {
        data() {
            return {
                // 尺寸监听开启状态
                qy_resizeListenerEnable: false,
                // 尺寸监听处理集合, key是元素选择器, value是回调函数
                qy_resizeListenerMap: {},
                // 尺寸监听对于元素的高度, 用来判断是否需要触发
                qy_resizeElementHeight: {},
            };
        },
        methods: {
            // 获取组件ref
            getRef(refName) {
                return joEl.getRef(this, refName);
            },
            // 刷新页面
            reloadPage() {
                window.location.reload();
            },
            // 元素高度变化监听
            registerElementHeightResize(selector, resizeFunc) {
                // 添加到监听集合
                this.qy_resizeListenerMap[selector] = resizeFunc;
                console.info('注册元素高度监听:', selector, this.qy_resizeListenerMap);
                // 监听没开启,则开启监听
                if (!this.qy_resizeListenerEnable) {
                    // 更新开启状态, 避免重复开启
                    this.qy_resizeListenerEnable = true;
                    const resizeCall = (k, func) => {
                        var height = this.qy_resizeElementHeight[k];
                        var newHeight = $(k).height();
                        if (height !== newHeight) {
                            // 更新元素高度
                            this.qy_resizeElementHeight[k] = newHeight;
                            try {
                                func();
                            } catch (e) {
                                console.error('元素高度监听回调函数执行异常: ', k, e);
                            }
                            console.info('resize: ', k);
                        }
                    }
                    // 窗口大小监听
                    $(window).resize(() => {
                        for (var k in this.qy_resizeListenerMap) {
                            var func = this.qy_resizeListenerMap[k];
                            if (typeof func == 'function') {
                                resizeCall(k, func);
                            }
                        }
                    });

                    // 定时轮训, 当窗口大小没变, 但是元素内容改变时, 不会触发window.resize, 通过轮训兜底
                    window.setInterval(() => {
                        for (var k in this.qy_resizeListenerMap) {
                            var func = this.qy_resizeListenerMap[k];
                            if (typeof func == 'function') {
                                resizeCall(k, func);
                            }
                        }
                    }, 500);
                }
            },
            // 格式化日期
            formatDate(t) {
                if (t) {
                    try {
                        return jo.formatDate(t);
                    } catch (e) {
                        console.error('格式化日期异常:', t, e);
                    }
                }
                return t;
            },
            // 格式化时间
            formatTime(t) {
                if (t) {
                    try {
                        return jo.formatTime(t);
                    } catch (e) {
                        console.error('格式化时间异常:', t, e);
                    }
                }
                return t;
            }
        }
    };
    // 视图页参数
    joEl.VUE_COMMON_VIEW = {
        data() {
            return {
                // 查询参数
                searchCondition: {},
                // 更多条件
                moreConditionFlag: false,
                // 表单校验规则
                checkRules: {},
                // 表单详情查询url
                formQueryUrl: '',
                // 新增url
                formInsertUrl: '',
                // 修改url
                formUpdateUrl: '',
                // 删除url
                deleteUrl: '',
                // 模板下载url
                templateUrl: '',
                // 导入url
                importUrl: '',
                // 导出url
                exportUrl: '',
                // 新增表单ref
                addFormRef: 'formRef',
                // 编辑表单ref
                editFormRef: 'formRef',

                // 新增表单标题
                addFormTitle: '新增',
                // 新增表单宽度
                addFormWidth: '75%',
                // 编辑表单标题
                editFormTitle: '编辑',
                // 编辑表单宽度
                editFormWidth: '75%',

                // 主键属性名
                primaryKey: 'id',
                // jo表格ref, 无特殊情况不需要改动
                joTableRef: 'joTableRef',
                // el表格ref, 无特殊情况不需要改动
                tableRef: 'tableRef',
                // 表格单选框值
                defaultTableRadio: '',
                // 表单数据
                formData: {},
                // 表单显示开关
                formShow: false,
                // 新增表单标识, 用来区分表单弹层是新增表单(1)还是编辑表单(2)
                formType: 1
            };
        },
        computed: {
            // 新增表单标识
            addFlag() {
                return this.formType == 1;
            },
            // 编辑表单标识
            editFlag() {
                return this.formType == 2;
            },
            // 表单ref
            formRef() {
                if (this.formType === 1) {
                    return this.addFormRef;
                } else if (this.formType === 2) {
                    return this.editFormRef;
                } else {
                    return 'formRef';
                }
            },
            // 表单标题
            formTitle() {
                if (this.formType === 1) {
                    return this.addFormTitle;
                } else if (this.formType === 2) {
                    return this.editFormTitle;
                } else {
                    return '';
                }
            },
            // 表单宽度
            formWidth() {
                if (this.formType === 1) {
                    return this.addFormWidth;
                } else if (this.formType === 2) {
                    return this.editFormWidth;
                } else {
                    return '75%';
                }
            },
        },
        methods: {
            // ************ 回调函数 start ************
            // ************ 回调函数 start ************
            // ************ 回调函数 start ************
            // 新增表单数据初始化前的处理
            handle_form_init_data_add(data) {

            },
            // 编辑表单数据初始化前的处理, ajax取到数据后
            handle_form_init_data_edit(data) {

            },
            // 新增表单提交前数据处理, 表单校验通过后,ajax请求前
            handle_form_data_before_submit_add(data) {

            },
            // 编辑表单提交前数据处理, 表单校验通过后,ajax请求前
            handle_form_data_before_submit_edit(data) {

            },
            // 关闭新增表单后的回调
            close_add_form_after() {

            },
            // 关闭编辑表单后的回调
            close_edit_form_after() {

            },
            // 新增成功后处理, 参数为后端返回结果
            insert_success_after(json) {
                jo.showSuccessMsg('保存成功~');
                // 刷新列表页
                this.list_refresh();
                // 关闭弹层
                this.close_add_form();
            },
            // 新增成功后处理, 参数为后端返回结果
            update_success_after(json) {
                jo.showSuccessMsg('更新成功~');
                // 刷新列表页
                this.list_refresh();
                // 关闭弹层
                this.close_add_form();
            },
            // 删除成功后处理
            delete_success_after(json) {
                jo.showSuccessMsg('删除成功~');
                // 刷新列表页
                this.list_refresh();
            },
            // 导入成功后处理
            import_success_after(json, file, data) {
                jo.showSuccessMsg('导入成功~');
                // 刷新列表页
                this.list_refresh();
            },
            // ************ 回调函数 end ************
            // ************ 回调函数 end ************
            // ************ 回调函数 end ************
            // jo表格ref
            list_jo_table_ref(joTableRef) {
                return this.getRef(joTableRef || this.joTableRef);
            },
            // 表格ref
            // list_table_ref() {
            //     return this.getRef(this.tableRef);
            // },
            // 新增表单ref
            add_form_ref() {
                return this.getRef(this.addFormRef);
            },
            // 编辑表单ref
            edit_form_ref() {
                return this.getRef(this.editFormRef);
            },
            // 获取选中项
            getCheckedItemArr(joTableRef) {
                // 拿到jo表格实例
                var joTable = joTableRef ? this.getRef(joTableRef) : this.list_jo_table_ref();
                if (!joTable) {
                    console.error('[列表页组件] 获取jo表格ref失败.', joTableRef);
                    return [];
                }
                return joTable.getCheckedItemArr();
            },
            // 获取选中项id集合
            getCheckedItemIdArr(joTableRef, idFieldName) {
                var arr = [];
                var _this = this;
                jo.forEach(this.getCheckedItemArr(joTableRef), function (item) {
                    arr.push(item[idFieldName || _this.primaryKey]);
                });
                return arr;
            },
            // 排序{ column, prop, order }
            list_table_sort(sort) {
                this.list_jo_table_ref().tableSortChange(sort);
            },
            // 列表查询
            list_search() {
                this.list_jo_table_ref().goPage();
            },
            // 列表刷新
            list_refresh(joTableRef) {
                this.list_jo_table_ref(joTableRef).refresh();
            },
            // 打开新增弹层
            open_add_form() {
                this.formShow = true;
                // 重置该表单项，将其值重置为初始值，并移除校验结果
                var ref = this.add_form_ref();
                if (ref) {
                    ref.resetFields();
                }
            },
            // 打开编辑弹层
            open_edit_form() {
                this.formShow = true;
                // 重置该表单项，将其值重置为初始值，并移除校验结果
                var ref = this.edit_form_ref();
                if (ref) {
                    ref.resetFields();
                }
            },
            // 关闭新增弹层
            close_add_form() {
                this.formShow = false;
                // 关闭弹层后的处理
                if (typeof this.close_add_form_after == 'function') {
                    this.close_add_form_after();
                }
            },
            // 关闭编辑弹层
            close_edit_form() {
                this.formShow = false;
                // 关闭弹层后的处理
                if (typeof this.close_edit_form_after == 'function') {
                    this.close_edit_form_after();
                }
            },
            // 关闭表单弹层
            close_form() {
                if (this.formType === 1) {
                    this.close_add_form();
                } else if (this.formType === 2) {
                    this.close_edit_form();
                } else {
                    console.error('未知的表单类型(formType):' + this.formType);
                }
            },
            // 初始化新增表单数据
            init_form_data_add() {
                var data = {};
                if (typeof this.handle_form_init_data_add == 'function') {
                    this.handle_form_init_data_add(data);
                }
                this.formData = data;
            },
            // 初始化编辑表单数据
            init_form_data_edit(item) {
                if (!this.formQueryUrl) {
                    console.error('表单详情查询URL无效,请检查配置项[formQueryUrl]');
                    return;
                }
                var _this = this;
                // 先清空, 在查询, 避免错乱
                this.formData = {};
                jo.postJson(this.formQueryUrl, {body: item[this.primaryKey]}).success(function (json) {
                    if (typeof _this.handle_form_init_data_edit == 'function') {
                        _this.handle_form_init_data_edit(json.data);
                    }
                    _this.formData = json.data || {};
                }).error(function (json) {
                    jo.showErrorMsg(json.info || '详情查询失败');
                });
            },

            // 新增
            list_add() {
                this.formType = 1;
                // 打开弹层
                this.open_add_form();
                // 初始化表单数据
                this.init_form_data_add();
            },
            // 编辑
            list_edit(item) {
                this.formType = 2;
                // 打开弹层
                this.open_edit_form();
                // 初始化表单数据
                this.init_form_data_edit(item);
            },
            // 删除
            list_delete(item) {
                var id = typeof item == 'object' ? item[this.primaryKey] : item;
                if (!id) {
                    console.error('[list_delete] 入参待删除项无效');
                    return;
                }
                this.delete_ids(id);
            },
            delete_ids(ids) {
                if (!this.deleteUrl) {
                    console.error('[delete_ids] 删除URL无效,请检查配置项[deleteUrl]');
                    return;
                }
                if (!ids) {
                    console.error('[delete_ids] 入参待删除项无效');
                    return;
                }
                var _this = this;
                jo.confirm('您确定要删除选中项嘛?', function (json) {
                    jo.postJson(_this.deleteUrl, {body: ids}).success(function (json) {
                        _this.delete_success_after(json);
                    }).error(function (json) {
                        jo.showErrorMsg(json.info || '删除失败');
                    });
                });
            },
            // 批量删除
            list_delete_batch() {
                var ids = this.getCheckedItemIdArr().join(',');
                if (!ids) {
                    jo.showTipsMsg('请选择待删除项~');
                    return;
                }
                this.delete_ids(ids);
            },
            // 批量删除模板方法,
            // table:(必填)表格ref属性, deleteUrl:(必填)删除url, idField:id字段名
            // noCheckedMsg:没有选中项时提示文案, confirmMsg:删除前确认文案
            // successCall:删除成功回调, errorCall:删除失败回调
            list_delete_template(param) {
                param.noCheckedMsg = param.noCheckedMsg || '请选择待删除项~';
                param.confirmMsg = param.confirmMsg || '您确定要删除选中项嘛?';
                param.successMsg = param.successMsg || '删除成功~';
                param.errorMsg = param.errorMsg || '删除失败';
                var ids = param.ids;
                if (!ids) {
                    if (!param.table) {
                        console.error('待删除ids或表格ref无效,请检查配置项[table]或[ids]');
                        return;
                    }
                    // 获取待删除id集合
                    ids = this.getCheckedItemIdArr(param.table, param.idField).join(',');
                    if (!ids) {
                        jo.showTipsMsg(param.noCheckedMsg);
                        return;
                    }
                }
                if (!param.deleteUrl) {
                    console.error('删除URL无效,请检查配置项[deleteUrl]');
                    return;
                }
                jo.confirm(param.confirmMsg, () => {
                    jo.postJson(param.deleteUrl, {body: ids}).success((json) => {
                        if (typeof param.successCall == 'function') {
                            param.successCall(json);
                        } else {
                            jo.showSuccessMsg(param.successMsg);
                            // 刷新表格
                            this.list_refresh(param.table);
                        }
                    }).error((json) => {
                        if (typeof param.errorCall == 'function') {
                            param.errorCall(json);
                        } else {
                            jo.showErrorMsg(json.info || param.errorMsg);
                        }
                    });
                });
            },
            // 表单保存
            form_save() {
                if (this.formType === 1) {
                    this.form_insert();
                } else if (this.formType === 2) {
                    this.form_update();
                } else {
                    console.error('未知的表单类型(formType):' + this.formType);
                }
            },
            // 表单保存模板方法
            // data:(必填)表单数据, url:(必填)保存url, formRef:表单ref属性
            // successMsg:成功提示文案, errorMsg:失败提示文案
            // successCall:删除成功回调, errorCall:删除失败回调
            form_save_template(param) {
                if (!jo.assert.object(param.data, '待保存数据为空')
                    || !jo.assert.valid(param.url, '表单保存URL为空')) {
                    return;
                }
                param.successMsg = param.successMsg || '保存成功~';
                param.errorMsg = param.errorMsg || '保存失败';
                var run = function () {
                    jo.postJson(param.url, {body: param.data}).success((json) => {
                        if (typeof param.successCall == 'function') {
                            param.successCall(json);
                        } else {
                            jo.showSuccessMsg(param.successMsg);
                        }
                    }).error((json) => {
                        if (typeof param.errorCall == 'function') {
                            param.errorCall(json);
                        } else {
                            jo.showErrorMsg(json.info || param.errorMsg);
                        }

                    });
                };
                if (param.formRef) {// ref有效则表单校验
                    var ref = typeof param.formRef == 'object' ? param.formRef : this.getRef(param.formRef);
                    if (!ref) {
                        console.error('[form_save_template] 获取表单失败:', param.formRef);
                        jo.showErrorMsg('获取表单失败');
                        return;
                    }
                    ref.validate((success, invalidFields) => {
                        if (success) {
                            run();
                        } else {
                            console.info('[form_save_template] 表单[%s]校验失败,非法字段:', param.formRef, invalidFields);
                        }
                    });
                } else {// 直接保存
                    run();
                }
            },
            // 表单新增
            form_insert() {
                var _this = this;
                var param = JSON.parse(JSON.stringify(this.formData));
                this.add_form_ref().validate(function (success, invalidFields) {
                    // invalidFields参数示例: [{field: "code",fieldValue: "",message: "职位编号必填"}]
                    if (success) {
                        if (typeof _this.handle_form_data_before_submit_add == 'function') {
                            _this.handle_form_data_before_submit_add(param);
                        }
                        jo.postJson(_this.formInsertUrl, {body: param}).success(function (json) {
                            _this.insert_success_after(json);
                        }).error(function (json) {
                            jo.showErrorMsg(json.info || '保存失败');
                        });
                    } else {
                        console.info('[form_insert] 表单校验失败,非法字段:', invalidFields);
                    }
                });
            },
            // 表单修改
            form_update() {
                var _this = this;
                var param = JSON.parse(JSON.stringify(this.formData));
                this.edit_form_ref().validate(function (success, invalidFields) {
                    // invalidFields参数示例: [{field: "code",fieldValue: "",message: "职位编号必填"}]
                    if (success) {
                        if (typeof _this.handle_form_data_before_submit_edit == 'function') {
                            _this.handle_form_data_before_submit_edit(param);
                        }
                        jo.postJson(_this.formUpdateUrl, {body: param}).success(function (json) {
                            _this.update_success_after(json);
                        }).error(function (json) {
                            jo.showErrorMsg(json.info || '更新失败');
                        });
                    } else {
                        console.info('[form_update] 表单校验失败,非法字段:', invalidFields);
                    }
                });
            },
            // 导入excel
            list_import_excel(files) {
                if (this.importUrl) {
                    jo.postFile(this.importUrl, files[0], {}, (json, file, data) => {
                        this.import_success_after(json, file, data);
                    }, (json, file, data) => {
                        jo.showErrorMsg(json.info || '导入失败!');
                        console.warn('[list_import_excel] 导入失败', json, file, data);
                    });
                } else {
                    jo.showErrorMsg('导入URL配置[importUrl]无效!');
                }
            },
            // 导出excel
            list_export_excel() {
                if (this.exportUrl) {
                    jo.newWindow(this.exportUrl);
                } else {
                    jo.showErrorMsg('导出URL配置[exportUrl]无效!');
                }
            },
        }
    };
})();